// Created: Dec 13, 2014
//
// Author: wjinwen.1988@gmail.com

#include "chi/net/acceptor.h"

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

#include <functional>

#include "chi/net/event_loop.h"
#include "chi/net/inet_address.h"

namespace chi {
namespace net {

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenaddr) 
  : loop_(loop),
    accept_socket_(Socket::CreateSocketCloseOnExec()),
    accept_channel_(loop, accept_socket_.fd()),
    listenning_(false),
    idle_fd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) {
  assert(idle_fd_ >= 0);
  accept_socket_.SetReuseAddress(true);
  accept_socket_.SetSoBlocking(false);   // set nonblocking
  accept_socket_.BindAddress(listenaddr);
  accept_channel_.SetReadCallback(std::bind(&Acceptor::HandleRead, this));
}

Acceptor::~Acceptor() {
  ::close(idle_fd_);
}
    
void Acceptor::set_new_conn_cb(const NewConnCallback& cb) {
    new_conn_cb_ = cb;
}

void Acceptor::Listen() {
  listenning_ = true;
  accept_socket_.Listen();
  accept_channel_.EnableReading();
}

void Acceptor::StopListen() {
  accept_channel_.DisableAll();
}

void Acceptor::HandleRead() {
  InetAddress peeraddr;
  int connfd = accept_socket_.Accept(&peeraddr);
  if (connfd >= 0) {
    if (new_conn_cb_) {
      new_conn_cb_(connfd, peeraddr);
    } else {
     ::close(connfd); 
    }
  } else {
    if (errno == EMFILE) {
      ::close(idle_fd_);
      idle_fd_ = ::accept(accept_socket_.fd(), NULL, NULL);
      ::close(idle_fd_);
      idle_fd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
    }
  }
}

}
}  
